package com.white.hot.controller;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import org.apache.http.util.TextUtils;
import org.springframework.web.context.ContextLoader;

import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.white.hot.dto.MaxRecord;
import com.white.hot.dto.WsClientBean;
import com.white.hot.dto.WsOutCountData;
import com.white.hot.dto.WsOutListData;
import com.white.hot.dto.WsResp;
import com.white.hot.service.MaxRecordService;
import com.white.hot.service.impl.MaxRecordServiceImpl;
/**
 * @ServerEndpoint 注解是一个类层次的注解，它的功能主要是将目前的类定义成一个websocket服务器端,
 *                 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
 */
@ServerEndpoint(value="/websocket")
public class WebSocketController
{
	private static MaxRecordService maxService;
	
	// concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话，可以使用Map来存放，其中Key可以为用户标识
	private static ConcurrentMap<String, WebSocketController> webSocketSet = new ConcurrentHashMap<String, WebSocketController>();
	private static ConcurrentMap<String, WsClientBean> deviceList = new ConcurrentHashMap<String, WsClientBean>();

	// 与某个客户端的连接会话，需要通过它来给客户端发送数据
	private Session session;
	private static WebSocketController manager;

	/**
	 * 连接建立成功调用的方法
	 *
	 * @param session
	 *            可选的参数。session为与某个客户端的连接会话，需要通过它来给客户端发送数据
	 */
	@OnOpen
	public void onOpen(Session session)
	{
		this.session = session;
		session.getAsyncRemote();
		System.out.println("有新连接加入！");
	}

	/**
	 * 连接关闭调用的方法
	 */
	@OnClose
	public void onClose()
	{
		System.out.println("关闭一条连接");
		try
		{
			if (session != null)
			{
				String id = String.valueOf(session.hashCode());
				if (!TextUtils.isEmpty(id))
				{
					webSocketSet.remove(String.valueOf(session.hashCode())); // 从set中删除
					deviceList.remove(String.valueOf(session.hashCode()));
					respCount();
				}
			}
		} catch (Exception e)
		{
			System.out.println("onClose异常");
		}
	}

	String MANAGER_ST_MD = "ws-ss";
	String MANAGER_ED_MD = "ws-se";
	String MANAGER_CT_MD = "ws-ct";
	String MANAGER_MAXCT_MD = "ws-maxct";
	String MANAGER_LT_MD = "ws-list";

	String MANAGER_CLIENT_PREFIX = "cli-";
	String MANAGER_OPEN_LIGHT_MD = "cli-0";
	String MANAGER_OPEN_LOG_MD = "cli-1";
	String MANAGER_CLOSE_APP_MD = "cli-2";

	String MANAGER_ST_MD_RESP = "ws-ss-sc";
	String MANAGER_ED_MD_RESP = "ws-se-sc";
	String MANAGER_CT_MD_RESP = "ws-ct-sc";
	String MANAGER_LT_MD_RESP = "ws-list-sc";

	String CLIENT_ST_MD = "0-c";
	String CLIENT_ED_MD = "1-c";
	String CLIENT_CT_MD = "2-c";
	String CLIENT_LT_MD = "3-c";

	String CLIENT_ST_MD_RESP = "0-c-sc";
	String CLIENT_ED_MD_RESP = "1-c-sc";
	String CLIENT_CT_MD_RESP = "2-c-sc";
	String CLIENT_LT_MD_RESP = "3-c-sc";

	Gson gson = new Gson();

	/**
	 * 收到客户端消息后调用的方法
	 *
	 * @param message
	 *            客户端发送过来的消息
	 * @param session
	 *            可选的参数
	 */
	@OnMessage
	public void onMessage(String message, Session session)
	{
		System.out.println("来自客户端的消息:" + message);
		// 处理管理页面逻辑
		if (MANAGER_ST_MD.equals(message))
		{
			manager = this;
			managerResp(MANAGER_ST_MD_RESP);
		} else if (MANAGER_ED_MD.equals(message))
		{
			manager = null;
		} else if (MANAGER_CT_MD.equals(message))
		{
			respCount();
			respMaxRecord();
		} else if (MANAGER_LT_MD.equals(message))
		{
			respList();
		} else if (message.startsWith(MANAGER_CLIENT_PREFIX))
		{
			int idx = message.indexOf("@");
			String cmd = message.substring(0, idx);
			String sessionId = message.substring(idx + 1);
			System.out.println(cmd + "-" + sessionId);
			if (MANAGER_OPEN_LIGHT_MD.equals(cmd))
			{
				WebSocketController target = webSocketSet.get(sessionId);
				if (target != null)
				{
					try
					{
						target.session.getBasicRemote().sendText(
								MANAGER_OPEN_LIGHT_MD);
					} catch (IOException e)
					{
					}
				}
			} else if (MANAGER_OPEN_LOG_MD.equals(cmd))
			{
				WebSocketController target = webSocketSet.get(sessionId);
				if (target != null)
				{
					try
					{
						target.session.getBasicRemote().sendText(
								MANAGER_OPEN_LOG_MD);
					} catch (IOException e)
					{
					}
				}
			} else if (MANAGER_CLOSE_APP_MD.equals(cmd))
			{
				WebSocketController target = webSocketSet.get(sessionId);
				if (target != null)
				{
					try
					{
						target.session.getBasicRemote().sendText(
								MANAGER_CLOSE_APP_MD);
					} catch (IOException e)
					{
					}
				}
			}
		}

		// 处理其他客户端
		if (CLIENT_ST_MD.equals(message))
		{
			sendMessage(CLIENT_ST_MD_RESP);
			webSocketSet.put(String.valueOf(session.hashCode()), this);
		} else if (CLIENT_ED_MD.equals(message))
		{
			webSocketSet.remove(String.valueOf(session.hashCode()));
			deviceList.remove(String.valueOf(session.hashCode()));
			sendMessage(dealList());
		}
		try
		{

			WsResp resp = gson.fromJson(message, WsResp.class);
			if (resp != null)
			{
				int type = resp.type;
				if (type == 0)
				{
					WsClientBean bean = resp.data;
					bean.seesionId = String.valueOf(session.hashCode());
					deviceList.put(String.valueOf(session.hashCode()), bean);
					respCount();
					if (bThread == null || !bThread.isRunning)
					{
						bThread = new BroadcastDeviceThread();
						bThread.start();
					}
					if (cleanThread == null)
					{
						cleanThread = new CleanThread();
						cleanThread.start();
					}
				}
			}
		} catch (JsonSyntaxException e)
		{

		}

		// for (WebSocketController item : webSocketSet)
		// {
		// try
		// {
		// item.sendMessage(message);
		// } catch (IOException e)
		// {
		// e.printStackTrace();
		// continue;
		// }
		// }
	}
	SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	private static MaxRecord maxRecord;
	private void respCount()
	{
		managerResp(MANAGER_CT_MD_RESP);
		WsOutCountData c = new WsOutCountData();
		int size = deviceList.size();
		if(size > maxDeviceRecord)
		{
			maxDeviceRecord = size;
			recordOperate();
		}
		c.count = size;
		String s = gson.toJson(c);
		managerResp(s);
	}

	private void respList()
	{
		managerResp(MANAGER_LT_MD_RESP);
		String s = dealList();
		managerResp(s);
	}
	
	private void respMaxRecord()
	{
		recordOperate();
		managerResp(MANAGER_MAXCT_MD);
		String a = gson.toJson(maxRecord);
		System.out.println(a);
		managerResp(a);
	}
	
	private final static Object synObj = new Object();
	private void recordOperate()
	{
		synchronized (synObj)
		{
			if(maxService == null)
				maxService = (MaxRecordService) ContextLoader.getCurrentWebApplicationContext().getBean("maxRecordService");
			List<MaxRecord> list = maxService.getList();
			if(list == null || list.size() == 0)
			{
				System.out.println("进来了");
				maxRecord = new MaxRecord();
				maxRecord.setMaxDevice(maxDeviceRecord);
				maxRecord.setTime(sdf.format(new Date(System.currentTimeMillis())));
				int mid = maxService.save(maxRecord);
				maxRecord.setId(mid);
			}else
			{
				maxRecord = list.get(0);
				if(maxDeviceRecord > maxRecord.getMaxDevice())
				{
					maxRecord.setMaxDevice(maxDeviceRecord);
					maxRecord.setTime(sdf.format(new Date(System.currentTimeMillis())));					
					maxService.updateItem(maxRecord);
				}
			}
		}
	}

	private String dealList()
	{
		Gson g = new Gson();
		if (deviceList.size() == 0)
		{
			WsOutListData d = new WsOutListData();
			d.data = new ArrayList<WsClientBean>();
			String s = g.toJson(d);
			return s;
		} else
		{
			// Set<Entry<String, WsClientBean>> st = deviceList.entrySet();
			//
			// List<WsClientBean> list = new ArrayList<WsClientBean>();
			// for (Iterator<Entry<String, WsClientBean>> it =
			// st.iterator();it.hasNext();)
			// {
			// Entry<String, WsClientBean> e = it.next();
			// WsClientBean v = e.getValue();
			// list.add(v);
			// }
			WsOutListData d = new WsOutListData();
			d.data = deviceList.values();
			String s = g.toJson(d);
			return s;
		}
	}

	/**
	 * 发生错误时调用
	 *
	 * @param session
	 * @param error
	 */
	@OnError
	public void onError(Session session, Throwable error)
	{
		System.out.println("错误发生");
		try
		{
			if (session != null)
			{
				String id = String.valueOf(session.hashCode());
				if (!TextUtils.isEmpty(id))
				{
					webSocketSet.remove(String.valueOf(session.hashCode())); // 从set中删除
					deviceList.remove(String.valueOf(session.hashCode()));
					respCount();
				}
			}
		}catch (Exception e){}		
	}

	/**
	 * 这个方法与上面几个方法不一样。没有用注解，是根据自己需要添加的方法。
	 *
	 * @param message
	 * @throws IOException
	 */
	public void sendMessage(String message)
	{
		try
		{
			this.session.getBasicRemote().sendText(message);
		} catch (IOException e){}
		// this.session.getAsyncRemote().sendText(message);
	}

	public void managerResp(String message)
	{
		try
		{
			if (manager != null)
			{
				manager.session.getBasicRemote().sendText(message);
			}
		} catch (IOException e)
		{
			e.printStackTrace();
		}
		// this.session.getAsyncRemote().sendText(message);
	}

	static long cleanStartTime;
	static CleanThread cleanThread;

	class CleanThread extends Thread
	{

		public CleanThread()
		{
			cleanStartTime = System.currentTimeMillis();
		}

		@Override
		public void run()
		{
			while (true)
			{
				long now = System.currentTimeMillis();
				if (now - cleanStartTime >= 20000)
				{
					cleanStartTime = now;
					if (webSocketSet.size() > 0)
					{
						for (Map.Entry<String, WebSocketController> entry : webSocketSet.entrySet())
						{
							String sid = entry.getKey();
							WebSocketController v = entry.getValue();
							if (!v.session.isOpen())
							{
								if (!TextUtils.isEmpty(sid))
								{
									deviceList.get(sid);
								}
							}
						}
						System.out.println("调用一次");
					}
				}
				try
				{
					Thread.sleep(50);
				} catch (Exception e)
				{
				}
			}
		}
	}
	
	static int maxDeviceRecord;
	static long startTime;
	private static BroadcastDeviceThread bThread;

	class BroadcastDeviceThread extends Thread
	{

		public boolean isRunning;

		public BroadcastDeviceThread()
		{
			startTime = System.currentTimeMillis();
		}

		public void run()
		{
			isRunning = true;
			while (true)
			{
				long now = System.currentTimeMillis();
				if (now - startTime >= 10000)
				{
					startTime = now;
					respList();
					respMaxRecord();
				}
				try
				{
					Thread.sleep(50);
				} catch (Exception e)
				{
				}
			}
		}
	}
}
